#ifndef TEMPLATE_MANAGER_MEMORY_HPP_
# define TEMPLATE_MANAGER_MEMORY_HPP_

# include <SFML/Graphics.hpp>

# include <map>
# include <string>
# include <utility>

# include "Template/Pattern/Singleton.hpp"

namespace		Manager
{
  template<typename T>
  class			Memory: public Pattern::Singleton<Manager::Memory<T> >
  {
	SingletonClass(typename Manager::Memory<T>);


  private:
	typedef std::pair<int unsigned, T *>	Block;
	typedef std::map<std::string, Block>	Bank;
	typedef std::pair<std::string, Block>	Pair;


  protected:
	Memory()
	  :	_Bank()
	{
	}
	Memory(Memory const &m)
	  :	_Bank()
	{
	  this->_CopyBank(m._Bank);
	}

	Memory const		&operator=(Memory const &m)
	{
	  if (this != &m)
		this->_CopyBank(m._Bank);

	  return (*this);
	}


  public:
	T					*Add(std::string const &label)
	{
	  typename Bank::iterator	cit, cite = this->_Bank.end();
	  T							*data = NULL;

	  if ((cit = this->_Bank.find(label)) != cite)
		{
		  ++cit->second.first;
		  data = cit->second.second;
		}
	  else
		{
		  this->_Bank.insert(Pair(label, Block(1u, (data = new T()))));
		}
	  return (data);
	}
	T					*Add(std::string const &label, T *data)
	{
	  typename Bank::iterator	cit, cite = this->_Bank.end();

	  if ((cit = this->_Bank.find(label)) != cite)
		++cit->second.first;
	  else
		this->_Bank.insert(Pair(label, Block(1u, data)));
	  return (data);
	}
	T					*Add(std::string const &label, T &data)
	{
	  typename Bank::iterator	cit, cite = this->_Bank.end();

	  if ((cit = this->_Bank.find(label)) != cite)
		{
		  ++cit->second.first;
		  return (cit->second.second);
		}

	  T		*dataret = new T(data);
	  this->_Bank.insert(Pair(label, Block(1u, dataret)));
	  return (dataret);
	}
	bool				Remove(std::string const &label)
	{
	  typename Bank::iterator	cit, cite = this->_Bank.end();

	  if ((cit = this->_Bank.find(label)) != cite && cit->second.first > 0)
		{
		  --cit->second.first;
		  return (true);
		}
	  return (false);
	}
	bool				Delete(std::string const &label)
	{
	  typename Bank::iterator	cit, cite = this->_Bank.end();

	  if ((cit = this->_Bank.find(label)) != cite)
		{
		  if (cit->second.first > 0)
			--cit->second.first;
		  if (cit->second.first == 0)
			{
			  delete cit->second.second;
			  this->_Bank.erase(cit);
			}
		  return (true);
		}
	  return (false);
	}


	void				Clean()
	{
	  typename Bank::iterator	cit = this->_Bank.begin(), cite = this->_Bank.end();

	  while (cit != cite)
		{
		  if (cit->second.first == 0)
			{
			  typename Bank::iterator	tmp = cit;

			  delete cit->second.second;
			  ++cit;
			  this->_Bank.erase(tmp);
			}
		  else
			++cit;
		}
	}
	void				Clear()
	{
	  typename Bank::iterator	cit = this->_Bank.begin(), cite = this->_Bank.end();

	  for ( ; cit != cite ; ++cit)
		delete cit->second.second;
	  this->_Bank.clear();
	}


	int unsigned		Size() const throw()
	{
	  return (this->_Bank.size());
	}


  protected:
	~Memory() throw()
	{
	  this->Clear();
	}


  protected:
	void					_CopyBank(Bank const &b)
	{
	  typename Bank::const_iterator	cit = b.begin(), cite = b.end();

	  for ( ; cit != cite ; ++cit)
		this->_Bank.insert(Pair(cit->first, Block(cit->second.first, new T(*cit->second.second))));
	}


  protected:
	Bank				_Bank;
  };


  typedef Memory<sf::Image>		ImageBank;
}

#endif
